#include <QtGui>
#include <QtCore/QVector>
#include <algorithm>
#include "TextWindow.h"
#include "Translate.h"
#include "Log.h"

TextWindow::TextWindow(const QString& windowName, MainWindow *mainWindow, QWidget *parent)
  : QWidget(parent), m_mainWindow(mainWindow)
{
  setWindowTitle(windowName);

  createWidgets();
  createActions();
  createToolbar();
  createLayout();
  connectSignals();
  this->m_origText->hide();
}

TextWindow::~TextWindow()
{
}

void TextWindow::createActions()
{
  m_actAddName = new QAction("Add name", this);
  m_actAddPhrase = new QAction("Add phrase", this);
  m_actAddHV = new QAction("Add HV", this);
  m_actSaveDictionary = new QAction("Save dictionary", this);
  m_actLoadDictionary = new QAction("Load dictionary", this);
  m_actRefresh = new QAction("Refresh", this);
  m_actRefresh->setShortcut(Qt::Key_F5);
  m_actFromClipboard = new QAction("From Clipboard", this);
}

void TextWindow::createWidgets()
{
  m_origText = new PhraseEdit; //m_origText->setReadOnly(true);
  m_hvText = new PhraseEdit; //m_hvText->setReadOnly(true);
  m_trText = new PhraseEdit; //m_trText->setReadOnly(true);
}

void TextWindow::createToolbar()
{
  m_tbDictionary = new QToolBar("Dictionary"); //addToolBar("Dictionary");
  m_tbDictionary->addAction(m_actAddName);
  m_tbDictionary->addAction(m_actAddPhrase);
  m_tbDictionary->addAction(m_actAddHV);
  m_tbDictionary->addAction(m_actSaveDictionary);
  m_tbDictionary->addAction(m_actLoadDictionary);
  m_tbDictionary->addAction(m_actRefresh);
  m_tbDictionary->addAction(m_actFromClipboard);
}

void TextWindow::createLayout()
{
  QVBoxLayout *vLayout = new QVBoxLayout;
  vLayout->addWidget(m_origText);
  vLayout->addWidget(m_hvText);
  QHBoxLayout *layout = new QHBoxLayout;
  layout->addWidget(m_trText);
  layout->addLayout(vLayout);

  QVBoxLayout *gLayout = new QVBoxLayout;
  gLayout->addWidget(m_tbDictionary);
  gLayout->addLayout(layout);
  setLayout(gLayout);
  //setCentralWidget(ctrWidget);

  //this->showMaximized();
}

void TextWindow::connectSignals()
{
  //connect(m_origText, SIGNAL(positionChanged(int)), this, SLOT(slot_origPosChanged(int)));
  //connect(m_trText, SIGNAL(positionChanged(int)), this, SLOT(slot_trPosChanged(int)));
  //connect(m_hvText, SIGNAL(positionChanged(int)), this, SLOT(slot_hvPosChanged(int)));
  connect(m_origText, SIGNAL(gotFocus()), this, SLOT(slot_origFocused()));
  connect(m_trText, SIGNAL(gotFocus()), this, SLOT(slot_trFocused()));
  connect(m_hvText, SIGNAL(gotFocus()), this, SLOT(slot_hvFocused()));

  connect(m_actAddName, SIGNAL(triggered()), this, SLOT(slot_addName()));
  connect(m_actAddPhrase, SIGNAL(triggered()), this, SLOT(slot_addPhrase()));
  connect(m_actAddHV, SIGNAL(triggered()), this, SLOT(slot_addHV()));
  connect(m_actRefresh, SIGNAL(triggered()), this, SLOT(slot_refresh()));
  connect(m_actSaveDictionary, SIGNAL(triggered()), this, SLOT(slot_saveDictionary()));
  connect(m_actLoadDictionary, SIGNAL(triggered()), this, SLOT(slot_loadDictionary()));
  connect(this, SIGNAL(refreshHighlight(int,int,int,int,int,int)), this, SLOT(slot_refreshHighlight(int,int,int,int,int,int)));
  connect(m_actFromClipboard, SIGNAL(triggered()), this, SLOT(slot_fromClipboard()));
}

void TextWindow::setOriginalText(const QString& text)
{
  Log::m_log << "set original length=" << text.length() << endl;
  disconnect(m_origText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_origPosChanged(const QTextCursor&)));
  disconnect(m_trText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_trPosChanged(const QTextCursor&)));
  disconnect(m_hvText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_hvPosChanged(const QTextCursor&)));
  m_origText->setOriginalText(text);

  QVector<Dictionary*> d1, d2;
  d1 << &Translator::m_dic[2];
  d2 << &Translator::m_dic[0] << &Translator::m_dic[1] << &Translator::m_dic[2];

  Phrases pHV, pVI;
  QString hv = Phrases::parse(text, d1, m_hvText->m_phrase);
  QString vi = Phrases::parse(text, d2, m_trText->m_phrase);

  m_trText->setFontPointSize(20);
  m_hvText->setOriginalText(hv);
  m_trText->setOriginalText(vi);

  connect(m_origText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_origPosChanged(const QTextCursor&)));
  disconnect(m_trText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_trPosChanged(const QTextCursor&)));
  disconnect(m_hvText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_hvPosChanged(const QTextCursor&)));
  m_origText->setFocus();
}

void TextWindow::slot_origPosChanged(int pos)
{
  m_origText->highlight(pos,pos,pos,false);
  int p = m_hvText->m_phrase.findPhrase(pos);
  if (p != -1) {
    const Phrase& ph = m_hvText->m_phrase[p];
    Log::m_log << "hvPhrase = " << p << "\n";
    m_hvText->highlight(ph.m_start, ph.m_end, ph.m_start);
  }
  else
    Log::m_log << "hvPhrase not found\n";

  p = m_trText->m_phrase.findPhrase(pos);
  if (p != -1) {
    const Phrase& ph = m_trText->m_phrase[p];
    Log::m_log << "trPhrase = " << p << "\n";
    m_trText->highlight(ph.m_start, ph.m_end, ph.m_start);
  }
  else
    Log::m_log << "trPhrase not found\n";
}

void TextWindow::slot_trPosChanged(int pos)
{
  int p = m_trText->m_phrase.findPhrase1(pos);
  if (p != -1) {
    m_trText->highlight(m_trText->m_phrase[p].m_start, m_trText->m_phrase[p].m_end, pos, false);
    const Phrase& ph = m_trText->m_phrase[p];
    Log::m_log << "trPhrase = " << p << "\n";
    m_origText->highlight(ph.m_origStart, ph.m_origEnd, ph.m_origStart);

    int p1 = m_hvText->m_phrase.findPhrase(ph.m_origStart);
    int p2 = m_hvText->m_phrase.findPhrase(ph.m_origEnd);
    if (p1 != -1) {
      if (p2 != -1) m_hvText->highlight(m_hvText->m_phrase[p1].m_start, m_hvText->m_phrase[p2].m_end, m_hvText->m_phrase[p1].m_start);
      else m_hvText->highlight(m_hvText->m_phrase[p1].m_start, m_hvText->m_phrase.back().m_end, m_hvText->m_phrase[p1].m_start);
    }
  }
  else
    Log::m_log << "trPhrase not found\n";
}

void TextWindow::slot_hvPosChanged(int pos)
{
  int p = m_hvText->m_phrase.findPhrase1(pos);
  if (p != -1) {
    m_hvText->highlight(m_hvText->m_phrase[p].m_start, m_hvText->m_phrase[p].m_end, pos, false);
    const Phrase& ph = m_hvText->m_phrase[p];
    Log::m_log << "hvPhrase = " << p << "\n";
    m_origText->highlight(ph.m_origStart, ph.m_origEnd, ph.m_origStart);

    int p1 = m_trText->m_phrase.findPhrase(ph.m_origStart);
    int p2 = m_trText->m_phrase.findPhrase(ph.m_origEnd);
    if (p1 != -1) {
      if (p2 != -1) m_trText->highlight(m_trText->m_phrase[p1].m_start, m_trText->m_phrase[p2].m_end, m_trText->m_phrase[p1].m_start);
      else m_trText->highlight(m_trText->m_phrase[p1].m_start, m_trText->m_phrase.back().m_end, m_trText->m_phrase[p1].m_start);
    }
  }
  else
    Log::m_log << "hvPhrase not found\n";
}

void TextWindow::get_interval(const QTextCursor& c, int &min, int &max)
{
  int pos = c.position(), anchor = c.anchor();
  min = std::min(pos, anchor);
  max = std::max(pos, anchor);
  if (max > min) max--;
  Log::m_log << "pos=" << pos << " anchor=" << anchor << " min=" << min << " max=" << max << endl;
}

void TextWindow::slot_origPosChanged(const QTextCursor& c)
{
  Log::m_log << "--origPos changed--\n";
  int min, max;
  get_interval(c, min, max);

  m_origText->highlight(min,max,c.position(),false);
  int p1 = m_hvText->m_phrase.findPhrase(min);
  int p2 = m_hvText->m_phrase.findPhrase(max);
  if (p1 != -1) {
    const Phrase& ph1 = m_hvText->m_phrase[p1];
    const Phrase& ph2 = (p2!=-1) ? m_hvText->m_phrase[p2] : m_hvText->m_phrase.back();
    m_hvText->highlight(ph1.m_start, ph2.m_end, ph1.m_start);
  }
  else
    Log::m_log << "hvPhrase not found\n";

  p1 = m_trText->m_phrase.findPhrase(min);
  p2 = m_trText->m_phrase.findPhrase(max);
  if (p1 != -1) {
    const Phrase& ph1 = m_trText->m_phrase[p1];
    const Phrase& ph2 = (p2!=-1) ? m_trText->m_phrase[p2] : m_trText->m_phrase.back();
    m_trText->highlight(ph1.m_start, ph2.m_end, ph1.m_start);
  }
  else
    Log::m_log << "trPhrase not found\n";
}

void TextWindow::slot_trPosChanged(const QTextCursor& c)
{
  Log::m_log << "trPos changed\n";
  int min, max;
  get_interval(c, min, max);

  int p1 = m_trText->m_phrase.findPhrase1(min);
  int p2 = m_trText->m_phrase.findPhrase1(max);
  if (p1 != -1) {
    const Phrase& ph1 = m_trText->m_phrase[p1];
    const Phrase& ph2 = (p2!=-1) ? m_trText->m_phrase[p2] : m_trText->m_phrase.back();
    m_trText->highlight(ph1.m_start, ph2.m_end, c.position(), false);
    m_origText->highlight(ph1.m_origStart, ph2.m_origEnd, ph1.m_origStart);

    p1 = m_hvText->m_phrase.findPhrase(ph1.m_origStart);
    p2 = m_hvText->m_phrase.findPhrase(ph2.m_origEnd);
    if (p1 != -1) {
      const Phrase& ph1 = m_hvText->m_phrase[p1];
      const Phrase& ph2 = (p2!=-1) ? m_hvText->m_phrase[p2] : m_hvText->m_phrase.back();
      m_hvText->highlight(ph1.m_start, ph2.m_end, ph1.m_start);
    }
  }
  else
    Log::m_log << "trPhrase not found\n";
}

void TextWindow::slot_hvPosChanged(const QTextCursor& c)
{
  Log::m_log << "hvPos changed\n";
  int min, max;
  get_interval(c, min, max);

  int p1 = m_hvText->m_phrase.findPhrase1(min);
  int p2 = m_hvText->m_phrase.findPhrase1(max);
  if (p1 != -1) {
    const Phrase& ph1 = m_hvText->m_phrase[p1];
    const Phrase& ph2 = (p2!=-1) ? m_hvText->m_phrase[p2] : m_hvText->m_phrase.back();
    m_hvText->highlight(ph1.m_start, ph2.m_end, c.position(), false);
    m_origText->highlight(ph1.m_origStart, ph2.m_origEnd, ph1.m_origStart);

    p1 = m_trText->m_phrase.findPhrase(ph1.m_origStart);
    p2 = m_trText->m_phrase.findPhrase(ph2.m_origEnd);
    if (p1 != -1) {
      const Phrase& ph1 = m_trText->m_phrase[p1];
      const Phrase& ph2 = (p2!=-1) ? m_trText->m_phrase[p2] : m_trText->m_phrase.back();
      m_trText->highlight(ph1.m_start, ph2.m_end, ph1.m_start);
    }
  }
  else
    Log::m_log << "hvPhrase not found\n";
}

void TextWindow::slot_origFocused()
{
  Log::m_log << "orig focused" << endl;
//  connect(m_origText, SIGNAL(positionChanged(int)), this, SLOT(slot_origPosChanged(int)));
//  disconnect(m_trText, SIGNAL(positionChanged(int)), this, SLOT(slot_trPosChanged(int)));
//  disconnect(m_hvText, SIGNAL(positionChanged(int)), this, SLOT(slot_hvPosChanged(int)));

  connect(m_origText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_origPosChanged(const QTextCursor&)));
  disconnect(m_trText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_trPosChanged(const QTextCursor&)));
  disconnect(m_hvText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_hvPosChanged(const QTextCursor&)));
}

void TextWindow::slot_trFocused()
{
  Log::m_log << "tr focused" << endl;
//  disconnect(m_origText, SIGNAL(positionChanged(int)), this, SLOT(slot_origPosChanged(int)));
//  connect(m_trText, SIGNAL(positionChanged(int)), this, SLOT(slot_trPosChanged(int)));
//  disconnect(m_hvText, SIGNAL(positionChanged(int)), this, SLOT(slot_hvPosChanged(int)));

  disconnect(m_origText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_origPosChanged(const QTextCursor&)));
  connect(m_trText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_trPosChanged(const QTextCursor&)));
  disconnect(m_hvText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_hvPosChanged(const QTextCursor&)));
}

void TextWindow::slot_hvFocused()
{
  Log::m_log << "hv focused" << endl;
//  disconnect(m_origText, SIGNAL(positionChanged(int)), this, SLOT(slot_origPosChanged(int)));
//  disconnect(m_trText, SIGNAL(positionChanged(int)), this, SLOT(slot_trPosChanged(int)));
//  connect(m_hvText, SIGNAL(positionChanged(int)), this, SLOT(slot_hvPosChanged(int)));

  disconnect(m_origText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_origPosChanged(const QTextCursor&)));
  disconnect(m_trText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_trPosChanged(const QTextCursor&)));
  connect(m_hvText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_hvPosChanged(const QTextCursor&)));
}

void TextWindow::createPhraseDialog(NewPhraseDialog &d, bool chooseHV) const
{
  d.m_lineOrig->setText( m_origText->currentText());
  d.m_lineHV->setText( m_hvText->currentText());
  d.m_lineTR->setText( m_trText->currentText());
  d.set_chooseHV(chooseHV);
}

void TextWindow::slot_addName()
{
  NewPhraseDialog d;
  createPhraseDialog(d, true);
  d.setWindowTitle("Add a new name ...");
  d.slot_allCapital();
  int r = d.exec();
  Log::m_log << "ret=" << r << endl;
  if (r == QDialog::Accepted) {
    QString from = d.m_lineOrig->text().trimmed(), to = d.m_lineHV->text().trimmed();
    Translator::m_dic[0][from].insert(0, to);
    m_actSaveDictionary->setText("Save dictionary (changed)");
    Log::m_log << "add name = " << from << " --> " << to << endl;
  }
}

void TextWindow::slot_addPhrase()
{
  NewPhraseDialog d;
  createPhraseDialog(d, false);
  d.setWindowTitle("Add a new phrase ...");
//  d.slot_allCapital();
  int r = d.exec();
  Log::m_log << "ret=" << r << endl;
  if (r == QDialog::Accepted) {
    QString from = d.m_lineOrig->text().trimmed(), to = d.m_lineTR->text().trimmed();
    Translator::m_dic[1][from].insert(0, to);
    m_actSaveDictionary->setText("Save dictionary (changed)");
    Log::m_log << "add phrase = " << from << " --> " << to << endl;
  }
}

void TextWindow::slot_addHV()
{
  NewPhraseDialog d;
  createPhraseDialog(d, true);
  d.setWindowTitle("Add a new phrase ...");
//  d.slot_allCapital();
  int r = d.exec();
  Log::m_log << "ret=" << r << endl;
  if (r = QDialog::Accepted) {
    QString from = d.m_lineOrig->text().trimmed(), to = d.m_lineHV->text().trimmed();
    Translator::m_dic[2][from].insert(0, to);
    m_actSaveDictionary->setText("Save dictionary (changed)");
    Log::m_log << "add hv = " << from << " --> " << to << endl;
  }
}

void TextWindow::slot_refresh()
{
  int tr_s = m_trText->m_s, tr_e = m_trText->m_e;
  int hv_s = m_hvText->m_s, hv_e = m_hvText->m_e;
  int orig_s = m_origText->m_s, orig_e = m_origText->m_e;
  QString text = m_origText->toPlainText();
  int length = text.length();
  Log::m_log << "refreshing length=" << length << endl;
  setOriginalText(text);
  //emit refreshHighlight(tr_s, tr_e, hv_s, hv_e, orig_s, orig_e);
}

void TextWindow::slot_fromClipboard()
{
  QString text = QApplication::clipboard()->text();
  int length = text.length();
  Log::m_log << "Clipboard text length=" << length << endl;
  setOriginalText(text);
  //emit refreshHighlight(tr_s, tr_e, hv_s, hv_e, orig_s, orig_e);
}

void TextWindow::slot_refreshHighlight(int tr_s, int tr_e, int hv_s, int hv_e, int orig_s, int orig_e)
{
  disconnect(m_origText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_origPosChanged(const QTextCursor&)));
  disconnect(m_trText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_trPosChanged(const QTextCursor&)));
  disconnect(m_hvText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_hvPosChanged(const QTextCursor&)));
  m_trText->highlight(tr_s, tr_e, tr_s);
  m_hvText->highlight(hv_s, hv_e, hv_s);
  m_origText->highlight(orig_s, orig_e, orig_s);
  connect(m_origText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_origPosChanged(const QTextCursor&)));
  disconnect(m_trText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_trPosChanged(const QTextCursor&)));
  disconnect(m_hvText, SIGNAL(positionChanged(const QTextCursor&)), this, SLOT(slot_hvPosChanged(const QTextCursor&)));
  m_origText->setFocus();
}

void TextWindow::slot_saveDictionary()
{
  for (int i = 0; i < Translator::m_dic.size(); i++)
    Translator::m_dic[i].save(QString::number(i) + ".txt");
  m_actSaveDictionary->setText("Save dictionary");
}

void TextWindow::slot_loadDictionary()
{
  for (int i = 0; i < Translator::m_dic.size(); i++)
    Translator::m_dic[i].load(QString::number(i) + ".txt");
  m_actSaveDictionary->setText("Save dictionary");
}

// ================ PHRASE & PHRASES =======================
QString Phrases::parse(const QString& text, const QVector<Dictionary*>& dic, Phrases& p) {
  QString s;
  p.clear();
  int os = 0, oe = 0;
  int ss = 0, ee = 0;
  for (int i = 0; i < text.size(); ) {
    int j = i + 20;
    if (j > text.size()) j = text.size();
    for (; j > i; j--) {
      QString phrase = text.mid(i, j-i);
      QString tmp;
      for (int k = 0; k < dic.size(); k++) if (dic[k]->contains(phrase)) {
        tmp = dic[k]->value(phrase)[0];
        break;
      }
      if (!tmp.isEmpty()) {
        s += tmp+" ";
        ee = ss+tmp.length();
        p << Phrase(i, j-1, ss, ee, phrase, tmp);
        ss = ee+1;
        break;
      }
    }
    if (j == i) {
      s += text[i];
      ee = ss;
      p << Phrase(i, i, ss, ee, QString(text[i]), QString(text[i]));
      ss = ee+1;
    }
    //p.back().print();
    i = j > i ? j : i+1;
  }
  return s;
}

int Phrases::findPhrase(int pos) const
{
  int l = 0, r = size()-1;
  while (l <= r) {
    int x = (l+r)/2;
    const Phrase& ph = this->at(x);
    if (ph.m_origStart > pos) r = x-1;
    else if (ph.m_origEnd < pos) l = x+1;
    else return x;
  }
  return -1;
}

int Phrases::findPhrase1(int pos) const
{
  int l = 0, r = size()-1;
  while (l <= r) {
    int x = (l+r)/2;
    const Phrase& ph = this->at(x);
    if (ph.m_start > pos) r = x-1;
    else if (ph.m_end < pos) l = x+1;
    else return x;
  }
  return -1;
}

void Phrase::print() const
{
  Log::m_log << "phrase: [" << m_origStart << "," << m_origEnd << "] " << m_orig 
                      << " --> ["    << m_start << "," << m_end << "] " << m_phrase << endl;
}

// ================ PhraseEdit ======================
void PhraseEdit::slot_cursorPositionChanged()
{
  QTextCursor c = this->textCursor();
  int pos = c.position();
  emit positionChanged(pos);
  emit positionChanged(c);
  Log::m_log << "pos = " << pos << endl;
}

void PhraseEdit::highlight(int s, int e, int pos, bool background)
{
  Log::m_log << "highlight: " << s << " - " << e << endl;
  QTextCursor c = textCursor();

  c.setPosition(m_s);
  c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m_e-m_s+1);
  QTextCharFormat normal = c.charFormat();
  normal.setForeground(QBrush(Qt::black));
  normal.setBackground(QBrush(Qt::white));
  c.setCharFormat(normal);

  c.setPosition(s);
  c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, e-s+1);
  QTextCharFormat highlighted = c.charFormat();
  highlighted.setForeground(QBrush(!background ? Qt::blue : Qt::white));
  highlighted.setBackground(QBrush(background ? Qt::blue : Qt::white));
  c.setCharFormat(highlighted);

  m_s = s;
  m_e = e;
  c = textCursor(); c.setPosition(pos);
  if (background) {
    setTextCursor(c);
    ensureCursorVisible();
  }
}

void PhraseEdit::focusInEvent ( QFocusEvent * event )
{
  QTextEdit::focusInEvent(event);
  emit gotFocus();
}

QString PhraseEdit::currentText() const
{
  return this->toPlainText().mid(m_s, m_e-m_s+1);
}

// ================ NewPhraseDialog ==================
NewPhraseDialog::NewPhraseDialog(QWidget* parent) : QDialog(parent)
{
  m_btOK = new QPushButton("OK");
  m_btCancel = new QPushButton("Cancel");
  m_chkAllCapital = new QRadioButton("All Capitals");
  m_chkNumberCapital = new QRadioButton("Number of Capitals");
  m_lineNumber = new QLineEdit;
  m_lineNumber->setText("1");
  m_lineOrig = new QLineEdit;
  m_lineHV = new QLineEdit;
  m_lineTR = new QLineEdit;
  m_chkSeparateDictionary = new QCheckBox("Save to separate dictionary");

  QHBoxLayout *chkLayout = new QHBoxLayout;
  chkLayout->addWidget(m_chkAllCapital);
  chkLayout->addWidget(m_chkNumberCapital);
  chkLayout->addWidget(m_lineNumber);
  QHBoxLayout *btLayout = new QHBoxLayout;
  btLayout->addWidget(m_chkSeparateDictionary);
  btLayout->addWidget(m_btOK);
  btLayout->addWidget(m_btCancel);
  QVBoxLayout *layout = new QVBoxLayout;
  layout->addWidget(new QLabel("Original phrase"));
  layout->addWidget(m_lineOrig);
  layout->addWidget(new QLabel("HV phrase"));
  layout->addWidget(m_lineHV);
  layout->addWidget(new QLabel("VI phrase"));
  layout->addWidget(m_lineTR);
  layout->addLayout(chkLayout);
  layout->addLayout(btLayout);
  setLayout(layout);

  connect(m_btOK, SIGNAL(clicked()), this, SLOT(accept()));
  connect(m_btCancel, SIGNAL(clicked()), this, SLOT(reject()));
  connect(m_chkAllCapital, SIGNAL(clicked()), this, SLOT(slot_allCapital()));
  connect(m_chkNumberCapital, SIGNAL(clicked()), this, SLOT(slot_numberCapital()));
  connect(m_lineNumber, SIGNAL(textChanged(const QString&)), this, SLOT(slot_numberCapital()));
}

void NewPhraseDialog::slot_allCapital()
{
  QString tmp = m_chooseHV ? m_lineHV->text() : m_lineTR->text();
  for (int i = 0; i < tmp.size(); i++)
    if (i == 0 || QString(tmp[i-1]).trimmed().isEmpty())
      tmp[i] = QString(tmp[i]).toUpper()[0];
  if (m_chooseHV)
    m_lineHV->setText(tmp);
  else
    m_lineTR->setText(tmp);
}

void NewPhraseDialog::slot_numberCapital()
{
  if (!m_chkNumberCapital->isChecked()) return;
  QString tmp = m_chooseHV ? m_lineHV->text() : m_lineTR->text();
  bool ok;
  int k = m_lineNumber->text().toUInt(&ok), j = 0;
  if (!ok) k = 0;
  for (int i = 0; i < tmp.size(); i++)
    if (i == 0 || QString(tmp[i-1]).trimmed().isEmpty()) {
      tmp[i] = (j < k) ? QString(tmp[i]).toUpper()[0] : QString(tmp[i]).toLower()[0];
      j++;
    }
  if (m_chooseHV)
    m_lineHV->setText(tmp);
  else
    m_lineTR->setText(tmp);
}

void TextWindow::keyPressEvent(QKeyEvent *e) 
{
  Log::m_log << "Key pressed: key = " << dec << e->key()  << " modifier = 0x" << hex << e->modifiers() << dec << endl;

  if (e->key() == Qt::Key_Y && e->modifiers() == Qt::ControlModifier) {   // Ctrl+Y back
    this->showMinimized();
    m_mainWindow->setFocus();
    m_mainWindow->raise();
    m_mainWindow->showMaximized();
    m_mainWindow->show();
  }
  else
    QWidget::keyPressEvent(e);
}
